package main

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"path/filepath"

	"github.com/uptrace/bun"
	"github.com/uptrace/bun/dialect/sqlitedialect"
	"github.com/uptrace/bun/driver/sqliteshim"
	"github.com/xiusin/logger"
)

// App struct
type App struct {
	ctx       context.Context
	connectId int64 // 已连接的ID
	db        *DbClient
	_local    *bun.DB
}

type Connection struct {
	bun.BaseModel `bun:"table:connections"`

	ID          int64  `bun:",pk,autoincrement" json:"id"`
	ConnectType string `bun:"connect_type" json:"connect_type,omitempty"`
	Name        string `bun:"name" json:"name"`
	DBPath      string `json:"db_path,omitempty" bun:"db_path"`
	Host        string `json:"host,omitempty"`
	Port        int    `json:"port,omitempty"`
	Username    string `json:"username,omitempty"`
	Password    string `json:"password,omitempty"`
	Database    string `json:"database,omitempty"`
}

func (c *Connection) ToDSN() string {
	switch c.ConnectType {
	case "sqlite":
		return c.DBPath
	case "mysql":
		formatter := "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=True&loc=Local"
		return fmt.Sprintf(formatter, c.Username, c.Password, c.Host, c.Port, c.Database)
	case "pgsql":
		formatter := "postgres://%s:%s@%s:%d/%s?sslmode=disable&application_name=bun_db"
		return fmt.Sprintf(formatter, c.Username, c.Password, c.Host, c.Port, c.Database)
	default:
		panic("暂不支持该数据库")
	}
}

var BasePath = ""

func init() {
	path, err := os.Getwd()
	if err != nil {
		path = "./"
	}
	BasePath, _ = filepath.Abs(path)
	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		panic(err)
	}
	f, err := os.OpenFile(filepath.Join(path, "app.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	logger.SetOutput(f)
}

func NewApp() *App {
	sqlDB, err := sql.Open(sqliteshim.ShimName, fmt.Sprintf("file:%s?cache=shared", filepath.Join(BasePath, "connections.db")))
	if err != nil {
		logger.Print(err)
		panic(err)
	}
	db := bun.NewDB(sqlDB, sqlitedialect.New(), bun.WithDiscardUnknownColumns())
	_, err = db.NewCreateTable().Model(&Connection{}).Exec(context.Background())
	if err != nil {
		logger.Error(err)
	}
	return &App{db: NewDbClient(), _local: db}
}

// SetContext 设置上下文
func (a *App) SetContext(ctx context.Context) {
	a.ctx = ctx
	a.db.SetContext(ctx)
}

// Connections 获取连接列表
func (a *App) Connections() []Connection {
	var connections = []Connection{}
	a._local.NewSelect().Model(&connections).Scan(a.ctx)
	return connections
}

// SaveConnection 保存连接对象
func (a *App) SaveConnection(connection Connection) (int64, error) {
	_, err := a._local.NewInsert().Model(&connection).Exec(a.ctx)
	return connection.ID, err
}

// CopyConnection 复制链接对象
func (a *App) CopyConnection(connection Connection) (Connection, error) {
	connection.ID = 0
	connection.Name += "-Copy"

	_, err := a._local.NewInsert().Model(&connection).Exec(a.ctx)
	return connection, err
}

// DeleteConnection 删除连接
func (a *App) DeleteConnection(id int64) error {
	if a.connectId == id {
		a.Disconnect()
	}
	_, err := a._local.NewDelete().Model(&Connection{}).Where("id = ?", id).Exec(a.ctx)
	return err
}

// Test 测试可连接性
func (a *App) Test(connection Connection) error {
	return a.db.Connect(Config{DriverName: connection.ConnectType, Dsn: connection.ToDSN()}, true)
}

// Connect 连接数据库
func (a *App) Connect(connection Connection) (err error) {
	if connection.ID == 0 {
		if connection.ID, err = a.SaveConnection(connection); err != nil {
			return err
		}
	}
	if err = a.db.Connect(Config{DriverName: connection.ConnectType, Dsn: connection.ToDSN()}); err != nil {
		return
	}

	a.connectId = connection.ID
	return nil
}

// Disconnect 断开连接
func (a *App) Disconnect() {
	a.connectId = 0
	a.db.Disconnect()
}

// Databases 数据库列表
func (a *App) Databases() []string {
	return a.db.Databases()
}

// Tables 数据库表
func (a *App) Tables(database string) map[string][]string {
	var data = map[string][]string{}
	data["views"] = a.db.Views(database)
	data["tables"] = a.db.Tables(database)
	return data
}

// Fields 表字段
func (a *App) Fields(database, name string) []Field {
	return a.db.Fields(database, name)
}

// Records 数据记录
func (a *App) Records(database, name string, limit int) []map[string]any {
	return a.db.Records(database, name, limit)
}

// Total 记录总数
func (a *App) Total(database, name string) int64 {
	return a.db.Total(database, name)
}

// Delete 删除数据记录
func (a *App) Delete(database, name string, where string, binding []any, limit int64) bool {
	return a.db.Delete(database, name, where, binding, limit)
}

// Exec 执行查询SQL
func (a *App) Exec(database, name, sql string) []map[string]any {
	// return a.db.Exec(database, name, sql)
	return nil
}

func (a *App) DuplicateTable(database, table, newtable string) error {
	return a.db.DuplicateTable(database, table, newtable)
}

func (a *App) DropTable(database, table string) error {
	return a.db.DropTable(database, table)
}

// TruncateTable 截断表
func (a *App) TruncateTable(database, table string) error {
	return a.db.TruncateTable(database, table)
}

// RenameTable 重命名表
func (a *App) RenameTable(database, table, newTable string) error {
	return a.db.RenameTable(database, table, newTable)
}

// CreateTable 创建表
func (a *App) CreateTable() (string, error) {
	return "", nil
}
